#ifndef QUEUE_H
#define QUEUE_H
#include "../common/project.pb.h"
#include "../common/Helper.hpp"
#include <cassert>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <mutex>
using namespace std;

// 1. 队列描述数据类
struct Queue 
{
    using ptr = shared_ptr<Queue>;
    Queue(){}
    Queue(const string& qname, bool qdurable, bool qexclusive, bool qauto_delete,
        google::protobuf::Map<string, string> qargs)
        : name(qname), durable(qdurable), exclusive(qexclusive), 
        auto_delete(qauto_delete), args(qargs)
    {}

    // args存储键值对，在进行数据库存储的时候，组织字符串格式进行存储
    // 内部解析这种key=val&key=val，把数据存储到数据库中
    void SetArgs(const string& str_args)
    {
        // key=val&key=val
        vector<string> vec_args;
        size_t ret = StrHelper::split(str_args, "&", vec_args);
        for(auto& str : vec_args)
        {
            // 提取出来key value
            size_t pos = str.find('=');
            string key = str.substr(0, pos);
            string value = str.substr(pos + 1);
            args[key] = value;
        }
    }

    // 从args中把数据存储到字符串中
    string GetArgs()
    {
        string result = "";
        for(auto& kv : args)
            result += kv.first + "=" + kv.second + "&";
        return result;
    }
    string name;
    bool durable;
    bool exclusive;
    bool auto_delete;
    google::protobuf::Map<string, string> args;
};

using QueueMap = unordered_map<string, Queue::ptr>;
// 2. 队列数据持久化类
class QueueMapper
{
public:
    QueueMapper(const string& db_file)
        : _sql_helper(db_file)
    {
        string path = FileHelper::parentDirectory(db_file);
        FileHelper::createDirectory(path);
        assert(_sql_helper.open());
        CreateTable();
    }
    void CreateTable()
    {
        stringstream sql;
        sql << "create table if not exists queue_table(";
        sql << "name varchar(32) primary key, ";
        sql << "durable int, ";
        sql << "exclusive int, ";
        sql << "auto_delete int, ";
        sql << "args varchar(128));";
        assert(_sql_helper.exec(sql.str(), nullptr, nullptr));
    }
    void RemoveTable()
    {
        string sql = "drop table if exists queue_table;";
        _sql_helper.exec(sql, nullptr, nullptr);
    }
    bool Insert(Queue::ptr& qe)
    {
        stringstream sql;
        sql << "insert into queue_table values(";
        sql << "'" << qe->name << "', ";
        sql << qe->durable << ", ";
        sql << qe->exclusive << ", ";
        sql << qe->auto_delete << ", ";
        sql << "'" << qe->GetArgs() << "');";
        return _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    void Remove(const string& name)
    {
        stringstream sql;
        sql << "delete from queue_table where name=";
        sql << "'" << name << "';";
        _sql_helper.exec(sql.str(), nullptr, nullptr);
    }
    using QueueMap = unordered_map<string, Queue::ptr>;
    QueueMap Recovery()
    {
        QueueMap result;
        string sql = "select name, durable, exclusive, auto_delete, args from queue_table;";
        _sql_helper.exec(sql, selectCallback, &result);
        return result;
    }

private:
    static int selectCallback(void* arg,int numcol,char** row,char** fields) {
        QueueMap *result = (QueueMap*)arg;
        Queue::ptr mqp = make_shared<Queue>();
        mqp->name = row[0];
        mqp->durable = (bool)stoi(row[1]);
        mqp->exclusive = (bool)stoi(row[2]);
        mqp->auto_delete = (bool)stoi(row[3]);
        if (row[4]) mqp->SetArgs(row[4]);
        result->insert(make_pair(mqp->name, mqp));
        return 0;
    }

private:
    SqliteHelper _sql_helper;
};

// 3. 队列数据管理类
class QueueManager 
{
public:
    using ptr = shared_ptr<QueueManager>;
    QueueManager(const string &dbfile):_mapper(dbfile) 
    {
        _msg_queues = _mapper.Recovery();
    }
    bool declareQueue(const string &qname, 
        bool qdurable, 
        bool qexclusive,
        bool qauto_delete,
        const google::protobuf::Map<string, string> &qargs)
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _msg_queues.find(qname);
        if (it != _msg_queues.end()) 
            return true;
        Queue::ptr mqp = make_shared<Queue>();
        mqp->name = qname;
        mqp->durable = qdurable;
        mqp->exclusive = qexclusive;
        mqp->auto_delete = qauto_delete;
        mqp->args = qargs;
        if (qdurable == true) 
        {
            bool ret = _mapper.Insert(mqp);
            if (ret == false) 
                return false;
        }
        _msg_queues.insert(make_pair(qname, mqp));
        return true;
    }
    void deleteQueue(const string &name) 
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _msg_queues.find(name);
        if (it == _msg_queues.end()) 
            return ;
        if (it->second->durable == true) 
            _mapper.Remove(name);
        _msg_queues.erase(name);
    }
    Queue::ptr selectQueue(const string &name) 
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _msg_queues.find(name);
        if (it == _msg_queues.end())
            return Queue::ptr();
        return it->second;
    }
    QueueMapper::QueueMap allQueues()
    {
        unique_lock<mutex> lock(_mutex);
        return _msg_queues;
    }
    bool exists(const string &name) 
    {
        unique_lock<mutex> lock(_mutex);
        auto it = _msg_queues.find(name);
        if (it == _msg_queues.end()) 
            return false;
        return true;
    }
    size_t size() 
    {
        unique_lock<mutex> lock(_mutex);
        return _msg_queues.size();
    }
    void clear() 
    {
        _mapper.RemoveTable();
        _msg_queues.clear();
    }
private:
    mutex _mutex;
    QueueMapper _mapper;
    QueueMapper::QueueMap _msg_queues;
};
#endif